package art_of_java_concurrent_programming.lock.condition;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author lixiangke
 * @date 2018/1/30
 */
public class BoundedQueue<E> {

    Lock lock = new ReentrantLock(true);
    Condition notFull = lock.newCondition();
    Condition notEmpty = lock.newCondition();

    Object[] elements;

    int size;

    int addIndex, removeIndex;

    public BoundedQueue(int capacity) {
        this.elements = new Object[capacity];
    }

    public void add(E e) throws InterruptedException {
        lock.lock();
        try {
            // 防止过早或意外的通知，只有条件符合才能够退出循环
            while (size == elements.length) {
                notFull.await(); // 队列已满, 等待条件 not Full
            }
            elements[size] = e;
            if (++addIndex == elements.length) {
                addIndex = 0;
            }
            size++;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public E remove() throws InterruptedException {
        lock.lock();
        try {
            while (size == 0) {
                notEmpty.await();
            }
            E e = (E) elements[removeIndex];
            elements[removeIndex] = null;
            if (++removeIndex == elements.length) {
                removeIndex = 0;
            }
            size--;
            notFull.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }
}
